import os


from TemplateSolver import TemplateSolver
from str_color import bcolors
from Board import Board
from Frame import Frame
from ai_alg_generator import *
from ab_alg_generator import ABSolver


class GameManger:
    """
    该模块用于管理游戏流程, 包括记录分数, 设置对手, 重新游戏, 结束游戏, 它是用于管理Board(棋盘类的中间键)
    """

    def __init__(self, red_player = 1, blue_player: int = 2, current_round: int = 1, *args, **kw) -> None:
        """构造方法, 处理能传入基本形参以外, 还能够传入键值对具体为
        is_print_board: 是否打印棋盘; is_print_col/row: 是否打印行/列信息; is_print_final_board 是否打印结束后的棋盘;
        is_print_box 是否打印格子信息; is_recode_step 是否将步骤持久化到文件夹

        Args:
            red_player (int): 红方玩家数值
            blue_player (int): 蓝方玩家数值
            current_round (int): 先手玩家(必须为red_player 或 blue_player的值)
        """

        # if 'GUI_ON' in kw and kw['GUI_ON']:
        #     pygame.init()
        #     init_windows()
        #     self.screen = pygame.display.set_mode((700, 500))
        #     pygame.display.set_caption("激战点格棋")
        #     self.screen.fill((255, 255, 255))

        self.board = Board()
        self.red_player = red_player
        self.blue_player = blue_player
        self.red_score = 0
        self.blue_score = 0
        self.current_round = current_round
        self.valid_step = []
        self.score_step = []
        self.turn_times = 0
        self.other_attr = kw

    def restart_game(self, current_round, *args, **kw):
        """重新设置棋盘

        Args:
            current_round (int): 谁先手
        """
        self.board.reset_board()
        self.red_score = 0
        self.blue_score = 0
        self.current_round = current_round
        self.valid_step = []
        self.score_step = []
        self.turn_times = 0
        self.other_attr = dict(kw, **self.other_attr)

    def is_game_over(self):
        """
        检验游戏是否结束

        Returns:
            bool: 如果结束返回True, 反之为False
        """

        if self.red_score + self.blue_score == 25:
            if 'is_record_step' in self.other_attr and self.other_attr['is_record_step']:
                self.record_step()
            return True
        else:
            return False

    # 谁赢了
    def who_win(self):
        return self.red_player if self.red_score > self.blue_score else self.blue_player

    # def directly_cin(self, dir, x, y):
    #

    # 玩家输入
    def get_cin(self):
        # 1：得到人的输入进行落线同时判断是否合法
        # 2：判断是否捕获格子
        # 3：是否进行换手
        # 4：判断游戏是否结束
        # 5：同时判断赢家
        # 测试点
        print(
            f'{bcolors.WARNING}*<玩家{self.current_round}> {bcolors.OKBLUE}请输入{bcolors.OKGREEN}->{bcolors.RESET}', end='')
        try:
            in_s = input()
            # 如果输入了undo指令则执行操作并返回
            # 针对人人对战的undo
            if in_s == '!undo':
                if len(self.valid_step) < 1:
                    print(f'{bcolors.FAIL}***undo fail, the len of valid_step less than 1***{bcolors.RESET}')
                    raise
                self.undo_step()
                return False
            # 针对人机对战的undo
            if in_s == '!undo2':
                if len(self.valid_step) < 2:
                    print(f'{bcolors.FAIL}***undo fail, the len of valid_step less than 2***{bcolors.RESET}')
                    raise
                self.undo_step()
                self.undo_step()
                return False
            in_s = in_s.split()
            if len(in_s) != 3:
                raise
            dir, x, y = [int(i) for i in in_s]
        except:
            print(f"{bcolors.FAIL}x 输入非法{bcolors.RESET}")
            self.get_cin()
            return

        # 判断输入是否合法
        while True:
            if len(in_s) == 3:
                temp_flag = 0
                for i in in_s:
                    try:
                        int(i)
                    except:
                        print(f"{bcolors.FAIL}x 输入非法{bcolors.RESET}")
                        temp_flag = 1
                        in_s = input().split()
                        break
                if temp_flag == 1:
                    temp_flag = 0
                    continue
                dir, x, y = [int(i) for i in in_s]
            if self.is_illegal(dir, x, y):
                self.valid_step.append((self.current_round, dir, x, y))
                print(f'{bcolors.OKGREEN}当前步伐为{(dir, x, y)}{bcolors.RESET}')
                break
            print(f"{bcolors.FAIL}x 输入非法{bcolors.RESET}")
            in_s = input().split()
            if len(in_s) != 3:
                continue
            dir, x, y = [int(i) for i in in_s]

        self.board.set_line(self.current_round, dir, x, y)
        dir, x, y = -1, -1, -1
        # self.record_step(dir, x, y)
        self.change_round()

        if self.is_game_over():
            print(f'{bcolors.WARNING}获胜的玩家为:<{str(self.who_win())}号玩家>{bcolors.RESET}')
            if 'is_print_final_board' in self.other_attr and self.other_attr['is_print_final_board']:
                print('最终棋盘为:')
                self.print_board()
                print(
                    f'{bcolors.FAIL}#<玩家{self.red_player}> 得分: {self.red_score}\n{bcolors.OKBLUE}#<玩家{self.blue_player}> 得分: {self.blue_score}{bcolors.RESET}')
            return True
        else:
            return False

    def toggle_color(self):
        if self.current_round == self.red_player:
            self.current_round = self.blue_player
            return self.blue_player
        else:
            self.current_round = self.red_player
            return self.red_player

    # AI输入
    # 算法输出坐标
    def ai_cin(self, *args, **kw):
        """
        当传入不传入算法函数时, 走默认算法,
        当传入算法函数时, 走传入的算法函数
        """

        if 'use_solver' in kw and kw['use_solver']:

            if kw['solver'] == 'solver_1':
                dir, x, y = self.solver_1.solve(self.get_frame())
            if kw['solver'] == 'solver_2':
                dir, x, y = self.solver_2.solve(self.get_frame())
            while True:
                if self.is_illegal(dir, x, y):
                    break
                print("x AI非法输入")
                if kw['solver'] == 'solver_1':
                    dir, x, y = self.solver_1.solve(self.get_frame())
                if kw['solver'] == 'solver_2':
                    dir, x, y = self.solver_2.solve(self.get_frame())
            print(f'{bcolors.OKGREEN}当前步伐为{(dir, x, y)}{bcolors.RESET}')
            self.board.set_line(self.current_round, dir, x, y)
            # self.record_step(dir, x, y)
            self.valid_step.append((self.current_round, dir, x, y))
            self.change_round()
            if self.is_game_over():
                print(f'{bcolors.WARNING}获胜的玩家为:<{str(self.who_win())}号玩家>{bcolors.RESET}')
                if 'is_print_final_board' in self.other_attr and self.other_attr['is_print_final_board']:
                    print('最终棋盘为:')
                    self.print_board()
                    print(
                        f'{bcolors.FAIL}#<玩家{self.red_player}> 得分: {self.red_score}\n{bcolors.OKBLUE}#<玩家{self.blue_player}> 得分: {self.blue_score}{bcolors.RESET}')
                # print(self.valid_step)  # 打印有效步数
                return True
            else:
                return False

        dir = -1
        x = -1
        y = -1
        # 主算法
        # dir, x, y = Monte_Carlo(self.board)
        # 测试算法
        """
        当传入不传入算法函数时, 走默认算法,
        当传入算法函数时, 走传入的算法函数
        """
        # 当不传入参数时, 走默认算法
        if len(args) == 0:
            dir, x, y = Cupidity(self.board, self.get_frame())
            while True:
                if self.is_illegal(dir, x, y):
                    break
                print("x 非法输入")
                dir, x, y = Cupidity(self.board)
            self.board.set_line(self.current_round, dir, x, y)
            print(f'{bcolors.OKGREEN}当前步伐为{(dir, x, y)}{bcolors.RESET}')
            # self.record_step(dir, x, y)
            self.change_round()

            if self.is_game_over():
                print(f'{bcolors.WARNING}获胜的玩家为:<{str(self.who_win())}号玩家>{bcolors.RESET}')
                if 'is_print_final_board' in self.other_attr and self.other_attr['is_print_final_board']:
                    print('最终棋盘为:')
                    self.print_board()
                    print(
                        f'{bcolors.FAIL}#<玩家{self.red_player}> 得分: {self.red_score}\n{bcolors.OKBLUE}#<玩家{self.blue_player}> 得分: {self.blue_score}{bcolors.RESET}')
                return True
            else:
                return False
        # 当传入一个算法函数时
        elif len(args) == 1:
            # 构造解决者(传入一个算法函数)
            game_solver = TemplateSolver(args[0])
            # 调用solver的solve方法(需要传入当前帧)获得下一步步伐
            dir, x, y = game_solver.solve(self.get_frame())
            while True:
                if self.is_illegal(dir, x, y):
                    break
                print("x AI非法输入")
                dir, x, y = game_solver.solve(self.get_frame())
            self.board.set_line(self.current_round, dir, x, y)

            # self.record_step(dir, x, y)
            self.valid_step.append((self.current_round, dir, x, y))
            self.change_round()
            if self.is_game_over():
                print(f'{bcolors.WARNING}获胜的玩家为:<{str(self.who_win())}号玩家>{bcolors.RESET}')
                if 'is_print_final_board' in self.other_attr and self.other_attr['is_print_final_board']:
                    print('最终棋盘为:')
                    self.print_board()
                    print(
                        f'{bcolors.FAIL}#<玩家{self.red_player}> 得分: {self.red_score}\n{bcolors.OKBLUE}#<玩家{self.blue_player}> 得分: {self.blue_score}{bcolors.RESET}')
                # print(self.valid_step)  # 打印有效步数
                return True
            else:
                return False

    def change_round(self):
        """
        换手
        """
        score = self.board.check_box(self.current_round)
        self.score_step.append((self.current_round, score))

        if score == 0:
            if self.current_round == self.red_player:
                self.current_round = self.blue_player
            else:
                self.current_round = self.red_player
            self.turn_times += 1
        else:
            if self.current_round == self.red_player:
                self.red_score += score
            else:
                self.blue_score += score

    def record_step(self, **kw):
        """
        持久化步数信息到文件
        """
        # 这个地方目录可以改
        # file = open('D:/各类文档/点格棋/beta-flying/data/data.txt', 'a')
        # print('当前玩家为' + str(self.current_round) + '号玩家')
        # data = {'current_round': self.current_round,
        #         'dir': dir, 'x': x, 'y': y}
        # if dir == 0:
        #     print(Fore.RED + '现在走的位置为第' + str(x + 1) + '行，第' + str(y + 1) + '列')
        #     file.write(str(data) + '\n')
        #     file.close()
        # else:
        #     print('现在走的位置为第' + str(x + 1) + '列，第' + str(y + 1) + '行')
        #     file.write(str(data) + '\n')
        #     file.close()
        ####################

        if 'dir' in kw:
            if not os.path.exists(dir):
                os.mkdir(kw['dir'])
        if not os.path.exists('../data'):
            os.mkdir('../data')
        with open('../data/record.txt', 'a') as f:
            str = f"{self.valid_step}\n{self.score_step}\n---\n"
            f.write(str)

    def is_illegal(self, dir, x, y):
        """判断落线是否合法

        Args:
            dir (int): 方向坐标
            x (int): 位置坐标
            y (int): 位置坐标

        Returns:
            bool: 合法返回True 反之为 False
        """
        if not isinstance(dir, int) or not isinstance(x, int) or not isinstance(y, int):
            return False
        if dir not in (0, 1) and x > 5 or x < 0 or y > 4 or y < 0:
            return False
        elif self.board.line[dir][x][y] != 0:
            return False
        else:
            return True

    def undo_step(self):
        """
        撤销上一步
        """
        last_step = self.valid_step.pop()
        last_score = self.score_step.pop()
        self.board.line[last_step[1]][last_step[2]][last_step[3]] = 0
        self.current_round = last_step[0]
        if last_score[0] == self.red_player:
            self.red_score -= last_score[1]
        elif last_score[0] == self.blue_player:
            self.blue_score -= last_score[1]
        self.board.refresh_box()
        print(f'{bcolors.OKGREEN}***undo successfully, below is new***{bcolors.RESET}')

    def set_frame(self, frame: Frame, *args, **kw):
        """设置游戏帧, 用于初始化设置棋局, 如果 set_frame_completely = True, 意味着完全初始化棋盘, **但要保证frame中有所需要的信息**

        Args:
            frame (Frame): 帧描述
        """
        self.red_player = frame.red_player
        self.blue_player = frame.blue_player
        self.board = frame.current_board
        self.red_score = frame.red_score
        self.blue_score = frame.blue_score
        self.current_round = frame.current_round
        if 'set_frame_completely' in kw and kw['set_frame_completely']:
            self.valid_step = frame.valid_step
            self.score_step = frame.score_step
            self.other_attr = dict(frame.other_attr, **self.other_attr)
            self.turn_times = frame.turn_times

    def set_complete_frame(self, frame: Frame):
        """辅助完全导入游戏帧的方法为set_frame的拓展

        Args:
            frame (Frame): 完全帧
        """
        self.set_frame(frame, set_frame_completely=True)

    def get_complete_frame(self) -> Frame:
        """返回完全帧

        Returns:
            Frame: 完全帧描述
        """
        return Frame(self.red_player, self.blue_player, self.current_round, self.red_score, self.blue_score, self.board,
                     valid_step=self.valid_step, score_step=self.score_step, other_attr=self.other_attr,
                     turn_times=self.turn_times)

    def get_frame(self) -> Frame:
        """返回基本帧

        Returns:
            Frame: 基本帧描述
        """
        return Frame(self.red_player, self.blue_player, self.current_round, self.red_score, self.blue_score, self.board)

    def round_info_print(self):
        """
        回合信息打印, 用于显示控制台一些信息
        """
        print(f'{bcolors.WARNING}#下一个回合为:<{self.current_round}号玩家>{bcolors.RESET}')
        if "is_print_box" in self.other_attr and self.other_attr["is_print_box"]:
            print(f'{bcolors.WARNING}#当前捕获的格子{bcolors.RESET}')
            self.board.print_box()
        if "is_print_line" in self.other_attr and self.other_attr["is_print_line"]:
            print(f'{bcolors.WARNING}#当前棋盘局势{bcolors.RESET}')
            self.board.print_line()
        if "is_print_row" in self.other_attr and self.other_attr["is_print_row"]:
            self.board.print_row()
        if "is_print_col" in self.other_attr and self.other_attr["is_print_col"]:
            self.board.print_col()
        if "is_print_board" in self.other_attr and self.other_attr["is_print_board"]:
            self.print_board()

        print(
            f'{bcolors.FAIL}#<玩家{self.red_player}> 得分: {self.red_score}\n{bcolors.OKBLUE}#<玩家{self.blue_player}> 得分: {self.blue_score}{bcolors.RESET}')

    def print_board(self):
        """
        彩色打印棋盘(控制台)
        """

        for i in range(6):
            for j in range(5):
                if j == 4:
                    if self.board.line[0][i][j] == self.red_player:
                        print(f'*{bcolors.FAIL}--{bcolors.RESET}*')
                    elif self.board.line[0][i][j] == self.blue_player:
                        print(f'*{bcolors.OKBLUE}--{bcolors.RESET}*')
                    else:
                        print('*--*')
                else:
                    if self.board.line[0][i][j] == self.red_player:
                        print(f'*{bcolors.FAIL}--{bcolors.RESET}', end='')
                    elif self.board.line[0][i][j] == self.blue_player:
                        print(f'*{bcolors.OKBLUE}--{bcolors.RESET}', end='')
                    else:
                        print('*--', end='')
            if i == 5:
                continue
            for j in range(6):
                if j == 5:
                    if self.board.line[1][j][i] == self.red_player:
                        print(f'{bcolors.FAIL}|{bcolors.RESET}', end='')
                    elif self.board.line[1][j][i] == self.blue_player:
                        print(f'{bcolors.OKBLUE}|{bcolors.RESET}', end='')
                    else:
                        print('|', end='')

                elif self.board.line[1][j][i] == self.red_player:
                    print(
                        f'{bcolors.FAIL}|{bcolors.RESET}', end='')
                    if self.board.box[i][j] != 0 and self.board.box[i][j] == self.red_player:
                        print(
                            f'{bcolors.FAIL}r{int(self.board.box[i][j])}{bcolors.RESET}', end='')
                    elif self.board.box[i][j] != 0 and self.board.box[i][j] == self.blue_player:
                        print(
                            f'{bcolors.OKBLUE}b{int(self.board.box[i][j])}{bcolors.RESET}', end='')
                    else:
                        print('  ', end='')

                elif self.board.line[1][j][i] == self.blue_player:
                    print(f'{bcolors.OKBLUE}|{bcolors.RESET}', end='')
                    if self.board.box[i][j] != 0 and self.board.box[i][j] == self.red_player:
                        print(
                            f'{bcolors.FAIL}r{int(self.board.box[i][j])}{bcolors.RESET}', end='')
                    elif self.board.box[i][j] != 0 and self.board.box[i][j] == self.blue_player:
                        print(
                            f'{bcolors.OKBLUE}b{int(self.board.box[i][j])}{bcolors.RESET}', end='')
                    else:
                        print('  ', end='')
                else:
                    print('|  ', end='')
                if j == 5:
                    print()

    def auto_game_loop(self, *args, **kw):

        """
        当不传算法函数时游戏回合为PvP,
        当传入一个算法函数时为PvE,
        当传入两个算法函数时为EvE
        """
        # 用法
        # solver = ABSolver()
        # solver_ = TemplateSolver(negamax)
        # gameInstance = GameManger(*init_game(), is_print_board = True, is_print_final_board = True)
        # gameInstance.auto_game_loop(use_solver = True, solver_1 = solver, solver_2 = solver_, is_print_board = True, is_print_final_board = True)

        self.other_attr = dict(kw, **self.other_attr)
        if 'use_solver' in kw and kw['use_solver']:
            self.solver_1: TemplateSolver = kw['solver_1']
            if 'solver_2' in kw:
                self.solver_2: TemplateSolver = kw['solver_2']
            if 'solver_2' not in kw:
                while True:
                    if self.current_round == self.red_player:
                        if self.ai_cin(use_solver=True, solver="solver_1"):
                            break
                    else:
                        if self.get_cin():
                            break
                    self.round_info_print()
                return
            if 'solver_2' in kw:
                while True:
                    # if 'GUI_ON' in self.other_attr and self.other_attr['GUI_ON']:
                    #     shader_frame(self.get_frame(), self.screen)
                    if self.current_round == self.red_player:
                        if self.ai_cin(use_solver=True, solver="solver_1"):
                            break
                    else:
                        if self.ai_cin(use_solver=True, solver="solver_2"):
                            break
                    self.round_info_print()
                return self.who_win()
        if len(args) == 0:
            while True:
                if self.current_round == self.red_player:
                    if self.get_cin():
                        break
                else:
                    if self.get_cin():
                        break
                self.round_info_print()
            return
        elif len(args) == 1:
            while True:
                if self.current_round == self.red_player:
                    if self.ai_cin(args[0]):
                        break
                else:
                    if self.get_cin():
                        break
                self.round_info_print()
            return
        elif len(args) == 2:
            while True:
                if self.current_round == self.red_player:
                    if self.ai_cin(args[0]):
                        break
                else:
                    if self.ai_cin(args[1]):
                        break
                # 打印回合信息
                self.round_info_print()
            return

        else:
            print(f'{bcolors.FAIL}x 传参有误{bcolors.RESET}')
